package com.mvc.rpc;

import com.alibaba.fastjson2.JSON;
import com.mvc.common.Res;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

/**
 * 简单的远程方法反射调用
 * @author JayEinstein
 */
@RequestMapping(CallMethod.CALL_URL)
public class RemoteProcedureCallExport implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @PostMapping(value = {"", "/"})
    @ResponseBody
    public Res<String> call(@RequestBody CallMethod callMethod) throws Exception {

        String beanClass = callMethod.getBeanClass();
        String methodName = callMethod.getMethodName();

        Class<?> aClass = ClassUtils.forName(beanClass, ClassUtils.getDefaultClassLoader());

        Object bean = applicationContext.getBean(aClass);

        // 无参方法
        if (!callMethod.hadArg()) {
            Method method = ReflectionUtils.findMethod(aClass, methodName);
            Object val = ReflectionUtils.invokeMethod(method, bean);
            return Res.ok(JSON.toJSONString(val));
        }

        // 有参方法
        List<String> argClassPath = callMethod.parseArgClassPath();
        List<Class<?>> argClasses = new ArrayList<>();
        for (String path : argClassPath) {
            Class<?> arg = ClassUtils.forName(path, ClassUtils.getDefaultClassLoader());
            argClasses.add(arg);
        }

        // 获取执行方法
        Method declaredMethod = aClass.getDeclaredMethod(methodName, argClasses.toArray(new Class[]{}));
        declaredMethod.setAccessible(true);

        // 解析参数
        Type[] parameterTypes = declaredMethod.getGenericParameterTypes();
        Object[] vals = callMethod.parseArgValue(parameterTypes);

        Object val = declaredMethod.invoke(bean, vals);
        return Res.ok(JSON.toJSONString(val));
    }

}
